package com.theaf.core.utils;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author luojuan
 * @create 2017-12-27 9:18
 **/
public class PasswordUtils {

    private final static String ALGORITHM = "Md5";

    public static String encodePassword(String rawPass, Object salt) {
        String saltedPass = mergePasswordAndSalt(rawPass, salt, false);

        MessageDigest messageDigest = getMessageDigest();

        byte[] digest = messageDigest.digest(Utf8.encode(saltedPass));

        // "stretch" the encoded value if configured to do so
        for (int i = 1; i < 1; i++) {
            digest = messageDigest.digest(digest);
        }


        return new String(Hex.encode(digest));
    }

    private static String mergePasswordAndSalt(String password, Object salt, boolean strict) {
        if (password == null) {
            password = "";
        }

        if (strict && (salt != null)) {
            if ((salt.toString().lastIndexOf("{") != -1)
                    || (salt.toString().lastIndexOf("}") != -1)) {
                throw new IllegalArgumentException("Cannot use { or } in salt.toString()");
            }
        }

        if ((salt == null) || "".equals(salt)) {
            return password;
        }
        else {
            return password + "{" + salt.toString() + "}";
        }
    }

    private static final MessageDigest getMessageDigest() throws IllegalArgumentException {
        try {
            return MessageDigest.getInstance(ALGORITHM);
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(
                    "No such algorithm [" + ALGORITHM + "]");
        }
    }

    private static final class Utf8 {
        private static  final Charset CHARSET = Charset.forName("UTF-8");

        /**
         * Get the bytes of the String in UTF-8 encoded form.
         */
        public static byte[] encode(CharSequence string) {
            try {
                ByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));
                byte[] bytesCopy = new byte[bytes.limit()];
                System.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit());

                return bytesCopy;
            }
            catch (CharacterCodingException e) {
                throw new IllegalArgumentException("Encoding failed", e);
            }
        }

        /**
         * Decode the bytes in UTF-8 form into a String.
         */
        public static String decode(byte[] bytes) {
            try {
                return CHARSET.newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
            }
            catch (CharacterCodingException e) {
                throw new IllegalArgumentException("Decoding failed", e);
            }
        }
    }


    private static final class Hex {

        private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f' };

        public static char[] encode(byte[] bytes) {
            final int nBytes = bytes.length;
            char[] result = new char[2 * nBytes];

            int j = 0;
            for (int i = 0; i < nBytes; i++) {
                // Char for top 4 bits
                result[j++] = HEX[(0xF0 & bytes[i]) >>> 4];
                // Bottom 4
                result[j++] = HEX[(0x0F & bytes[i])];
            }

            return result;
        }

        public static byte[] decode(CharSequence s) {
            int nChars = s.length();

            if (nChars % 2 != 0) {
                throw new IllegalArgumentException(
                        "Hex-encoded string must have an even number of characters");
            }

            byte[] result = new byte[nChars / 2];

            for (int i = 0; i < nChars; i += 2) {
                int msb = Character.digit(s.charAt(i), 16);
                int lsb = Character.digit(s.charAt(i + 1), 16);

                if (msb < 0 || lsb < 0) {
                    throw new IllegalArgumentException(
                            "Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
                }
                result[i / 2] = (byte) ((msb << 4) | lsb);
            }
            return result;
        }

    }
}
